<template>
  <div style="width: 800px;height: 500px;position: relative;margin: 0 auto;">
    <div id="mountNode"></div>
    <Row type="flex" justify="center" :gutter="16" class="controlBar">
      <Col>
        <Button type="primary" @click="redraw">重绘</Button>
      </Col>
      <Col>
        <Button type="primary" @click="downloadFullImage">导出图片</Button>
      </Col>
    </Row>
  </div>
</template>

<script>
/**
 * (1 对象风格
 * (2 线的风格
 * (3 性能
 * (4 布局效果
 * (5 图例
 * (6 缩略图
 * (7 tooltip提示
 * (8 对象收起后自动布局
*/
import G6 from '@antv/g6'
export default {
  name: 'ViewGraph',
  data () {
    return {
      // 点集
      nodes: [
        {
          id: 'node1', // String，该节点存在则必须，节点的唯一标识
          label: 'SDATA.A11_SALES',
          type: 'bloodType',
          field: [
            {
              key: 'id',
              type: 'number(6)'
            },
            {
              key: 'name',
              type: 'VARCHAR(10)'
            }
          ]
        },
        {
          id: 'node2', // String，该节点存在则必须，节点的唯一标识
          label: 'DATA.A11_SALES',
          type: 'dice-er-box',
          style: {
            // fillOpacity: 0
          },
          attrs: [
            {
              key: 'key',
              type: 'varchar(255)'
            },
            {
              key: 'gender',
              type: 'enum(M, F)'
            },
            {
              key: 'birthday',
              type: 'date'
            },
            {
              key: 'hometown',
              type: 'varchar(255)'
            }
          ]
        }
      ],
      // 边集
      edges: [
        {
          source: 'node1', // String，必须，起始点 id
          target: 'node2',  // String，必须，目标点 id
          style: {
            endArrow: {
              path: 'M 0,0 L 8,4 L 8,-4 Z',
              fill: '#ececec'
            }
          },
          type: 'cubic-horizontal'
        },
        // {
        //   source: 'node2', // String，必须，起始点 id
        //   target: 'node2',  // String，必须，目标点 id
        //   style: {
        //     endArrow: {
        //       path: 'M 0,0 L 8,4 L 8,-4 Z',
        //       fill: '#ececec'
        //     }
        //   },
        //   type: 'hvh',
        //   sourceAnchor: 1,
        //   targetAnchor: 0,
        //   // loopCfg: {
        //   //   position: 'top',
        //   //   dist: 100
        //   // },
        // }
      ],
      graph: null,
      grid: null,
      minimap: null,
      legend: null, // 图例
      legendData: {
        nodes: [
          {
            id: 'type1',
            label: '源表',
            type: 'rect',
            size: [50, 30],
            style: {
              fill: '#eff4ff'
            }
          },
          {
            id: 'type2',
            label: '目标表',
            type: 'rect',
            size: [50, 30],
            style: {
              fill: 'orange'
            }
          }
        ],
        // edges: [
        //   {
        //     id: 'eType1',
        //     label: '关系1',
        //     type: 'cubic-horizontal'
        //   }
        // ]
      },
      toolbar: null, // 工具栏
      bloodData: [],
      bloodNodes: [],
      bloodEdges: []
    }
  },
  async mounted () {
    const { data: res } = await this.Api.get('/adp/adp/sql/getSqlBlood', { params: { taskId: 897, distance: '', direction: '' } })
    this.bloodNodes = res.data.filter(item => item.shape === 'er-rect')
    this.bloodEdges = res.data.filter(item => item.shape === 'edge')
    this.initGraph()
  },
  methods: {
    initGraph () {
      this.grid = new G6.Grid()
      this.minimap = new G6.Minimap({
        size: [200, 120],
        className: 'minimap',
        type: 'delegate' // 选项：'default'：渲染图上所有图形；'keyShape'：只渲染图上元素的 keyShape，以减少渲染成本；'delegate'：只渲染图上元素的大致图形，以降低渲染成本。渲染成本 'default' > 'keyShape' > 'delegate'。默认为 'default'
      })
      this.toolbar = new G6.ToolBar({
        position: { x: 10, y: 10 }
      })
      this.legend = new G6.Legend({
        data: this.legendData,
        // align: 'center',
        position: 'bottom',
        containerStyle: {
          fill: '#ccc',
          lineWidth: 1
        },
        padding: [30, 16, -22, 16]
      })
      this.graph = new G6.Graph({
        container: 'mountNode',
        width: 800,
        height: 500,
        modes: {
          default: ['drag-canvas', 'zoom-canvas', 'drag-node', {
            type: 'tooltip',
            formatText (model) {
              return model.label
            },
            shouldUpdate (e) {
              // console.log(e.item.getModel().id)
              return true
            },
            shouldBegin: e => {
              return true
            }
          }, 'click-select']  // 允许拖拽画布、放缩画布、拖拽节点、提示文本
        },
        defaultNode: {
          anchorPoints: [
            // 边的连接点
            [0, 0.5], // 左侧中间
            [1, 0.5]  // 右侧中间
          ]
        },
        plugins: [
          this.grid,
          this.minimap,
          this.legend,
          this.toolbar
        ],
        layout: {
          type: 'dagre',
          rankdir: 'LR', // layout 的方向。T：top；B：bottom；L：left；R：right
          // align: 'DL', // 节点对齐方式。默认值是 undefined，代表对齐到中心。U：upper；D：down；L：left；R：right
          nodesepFunc: () => 50,
          ranksepFunc: () => 80
        },
        fitView: false // 是否开启画布自适应。开启后图自动适配画布大小
      })
      const { registerNode, registerEdge } = G6
      // 自定义节点
      registerNode('bloodNode', {
        draw (cfg, group) {
          const keyshape = group.addShape('rect', {
            attrs: {
              width: 100,
              height: 30,
              fill: 'skyblue',
              stroke: '#000'
            }
          })
          return keyshape
        }
      })

      registerNode(
        'dom-node',
        {
          draw: (cfg, group) => {
            return group.addShape('dom', {
              attrs: {
                width: 100,
                height: 50,
                // 传入 DOM 的 html
                html: `
                  <dl>
                    <dt>标题</dt>
                    <dd>列表1</dd>
                    <dd>列表2</dd>
                  </dl>
                `
              },
              draggable: true
            })
          }
        },
        'single-node'
      )

      /**
       * typeName:该新节点类型名称
       * nodeDefinition:该新节点类型的定义。当有 extendedTypeName 时，没被复写的函数将会继承 extendedTypeName 的定义
       * extendedTypeName:被继承的节点类型，可以是内置节点类型名，也可以是其他自定义节点的类型名。extendedTypeName 未指定时代表不继承其他类型的节点
      */
      registerNode('blood', {
        draw (cfg, group) {
          const keyShape = group.addShape('rect', {
            attrs: {
              width: 100,
              height: 150,
              stroke: 'red'
            },
            draggable: true
          })
          group.addShape('rect', {
            attrs: {
              width: 100,
              height: 30,
              fill: 'rgb(69, 171, 250)'
            },
            draggable: true
          })
          group.addShape('text', {
            attrs: {
              y: 22,
              text: 'SHDATA',
              fill: '#fff'
            }
          })
          for (let i = 0; i < cfg.attrs.length; i++) {
            group.addShape('rect', {
              attrs: {
                width: 100,
                height: 30,
                y: (i + 1) * 30,
                fill: '#fff',
                stroke: '#ddd'
              }
            })
            group.addShape('text', {
              attrs: {
                text: 'ACCT_IND',
                fill: '#000',
                y: (i + 1) * `${i < 1 ? 52 : 41}`
                // 1 
              }
            })
          }
          return keyShape
        }
      })

      registerNode('bloodType', {
        draw (cfg, group) {
          const keyShape = group.addShape('rect', {
            attrs: {
              width: 100,
              height: (cfg.field.length + 1) * 30,
              stroke: '#91c051',
              radius: [2, 2, 0, 0]
            }
          })
          const titleGroup = group.addGroup({
            id: 'titleGroup'
          })
          titleGroup.addShape({
            type: 'rect',
            attrs: {
              width: 100,
              height: 30,
              fill: '#91c051',
              radius: [2, 2, 0, 0]
            },
            draggable: true
          })
          titleGroup.addShape({
            type: 'text',
            attrs: {
              y: 11,
              x: 25,
              textBaseline: 'top',
              text: 'SHDATA',
              fill: '#fff'
            },
            draggable: true
          })
          if (cfg.field && cfg.field.length) {
            const fieldGroup = group.addGroup({
              id: 'fieldGroup'
            })
            for (let i = 0; i < cfg.field.length; i++) {
              fieldGroup.addShape({
                type: 'rect',
                attrs: {
                  width: 100,
                  height: 30,
                  y: (i + 1) * 30,
                  // fill: '#fff'
                },
                draggable: true
              })
              fieldGroup.addShape({
                type: 'text',
                attrs: {
                  text: cfg.field[i].key,
                  x: 12,
                  y: 24 + (i + 1) * 28,
                  // y: 30 + (i + 1) * 20,
                  // textBaseline: 'top',
                  fill: '#000'
                },
                draggable: true
              })
            }
          }
          return keyShape
        }
      })

      const itemHeight = 30
      registerNode('dice-er-box', {
        draw (cfg, group) {
          const width = 100
          const height = 316
          const itemCount = 10
          const boxStyle = {
            stroke: '#d26b58',
            radius: 2
          }
          const { attrs, startIndex, selectedIndex } = cfg
          group.addShape("rect", {
            attrs: {
              fill: boxStyle.stroke,
              height: 30,
              width,
              radius: [boxStyle.radius, boxStyle.radius, 0, 0]
            },
            draggable: true
          })
          group.addShape('text', {
            attrs: {
              x: 6,
              y: 22,
              text: cfg.label,
              fill: '#fff',
              fontWeight: selectedIndex ? 500 : 100
            },
            draggable: true
          })
          const listContainer = group.addGroup({})
          listContainer.addShape({
            type: 'rect',
            attrs: {
              y: 30,
              width,
              height: 145 - 30,
              fill: '#fff'
            },
            draggable: true
          })
          if (attrs) {
            attrs.forEach((e, i) => {
              // listContainer.addShape({
              //   type: 'rect',
              //   attrs: {
              //     y: 70,
              //     width,
              //     height: 30,
              //     fill: '#ddd'
              //   },
              //   zIndex: 1
              // })
              listContainer.addShape('text', {
                attrs: {
                  x: 12,
                  y: 24 + (i + 1) * 28,
                  text: e.key,
                  fill: '#000'
                },
                zIndex: 9,
                draggable: true
              })
            })
          }
          const keyShape = group.addShape('rect', {
            attrs: {
              width,
              height: 145,
              ...boxStyle,
              radius: [2, 2, 0, 0]
            },
            draggable: true
          })
          return keyShape
        },
        getAnchorPoints() {
          return [
            [0, .5],
            [1, .5]
          ]
        }
      })
      // 自定义边
      registerEdge('hvh', {
        /**
         * @param  {Object} cfg 节点的配置项
         * @param  {G.Group} group 图形分组，节点中图形对象的容器
         * @return {G.Shape} 返回一个绘制的图形作为 keyShape，通过 node.get('keyShape') 可以获取
        */
        draw (cfg, group) {
          const startPoint = cfg.startPoint
          const endPoint = cfg.endPoint
          const shape = group.addShape('path', {
            attrs: {
              stroke: '#e0e0e0',
              // path: [
              //   ['M', startPoint.x, startPoint.y],
              //   ['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y], // 三分之一处
              //   ['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y], // 三分之二处
              //   ['L', endPoint.x, endPoint.y]
              // ],
              endArrow: {
                path: 'M 0,0 L 8,4 L 8,-4 Z',
                fill: '#ececec'
              },
              path: [
                ['M', startPoint.x, startPoint.y],
                ['C', startPoint.x, startPoint.y, startPoint.x - 50, startPoint.y + 200, endPoint.x, endPoint.y]
              ]
            },
            name: 'path-shape'
          })
          return shape
        }
      })
      // 鼠标经过事件
      // this.graph.on('node:mousemove', e => {
      //   this.graph.updateItem(e.item, {
      //     selectedIndex: true
      //   })
      // })
      // // 鼠标离开事件
      // this.graph.on('node:mouseleave', e => {
      //   this.graph.updateItem(e.item, {
      //     selectedIndex: false
      //   })
      // })
      // 节点点击事件
      this.graph.on('node:click', e => {
        const item = e.item
        this.graph.focusItem(item, true, {
          easing: 'easeCubic',
          duration: 500
        })
      })
      // 鼠标移入时触发
      this.graph.on('node:mouseenter', e => {
        const item = e.item
        this.graph.setAutoPaint(false) // 设置是否在更新/删除后自动重绘
        this.graph.getNodes().forEach(node => {
          this.graph.clearItemStates(node) // 清除元素状态，可以一次性清除多个状态
        })
        this.graph.setItemState(item, 'highlight', true) // 设置元素状态
        // this.graph.getEdges().forEach(edge => {
        //   if (edge.getSource().get('id') === item.get('id')) {
        //     this.graph.setItemState(edge.getTarget(), 'highlight', true)
        //   }
        // })
        // let _this = this
        // function deepHighlight (item) {
        //   _this.graph.getEdges().forEach(edge => {
        //     if (edge.getSource().get('id') === item.get('id')) {
        //       _this.graph.setItemState(edge.getTarget(), 'highlight', true)
        //       deepHighlight(edge.getTarget())
        //     }
        //   })
        // }
        // deepHighlight(item)
        this.graph.paint()
        this.graph.setAutoPaint(true) // 当图中元素更新，或视口变换时，是否自动重绘。建议在批量操作节点时关闭，以提高性能，完成批量操作后再打开
      })
      for (let i = 0; i < 50; i++) {
        this.nodes.push({ id: `node${i + 3}`, label: `${i + 3}`, type: 'rect' })
      }
      this.edges.push({ source: 'node1', target: 'node3', style: {endArrow: {path: 'M 0,0 L 8,4 L 8,-4 Z', fill: '#ececec'}}, type: 'cubic-horizontal' })
      this.edges.push({ source: 'node1', target: 'node4', style: {endArrow: {path: 'M 0,0 L 8,4 L 8,-4 Z', fill: '#ececec'}}, type: 'cubic-horizontal' })
      this.edges.push({ source: 'node2', target: 'node4', style: {endArrow: {path: 'M 0,0 L 8,4 L 8,-4 Z', fill: '#ececec'}}, type: 'cubic-horizontal' })
      for (let i = 0; i < 49; i++) {
        this.edges.push({ source: `node${i + 3}`, target: `node${i + 4}`, style: {endArrow: {path: 'M 0,0 L 8,4 L 8,-4 Z', fill: '#ececec'}}, type: 'cubic-horizontal' })
      }
      // this.graph.setTextWaterMarker('水印水印水印水印水印水印水印水印水印水印水印')
      // this.bloodEdges.map(item => {
      //   return { ...item, source: item.source.port, target: item.target.port }
      // })
      this.graph.data({nodes: this.nodes, edges: this.edges})
      this.graph.render()
      console.log(this.nodes)
      console.log(this.edges)
      console.log(this.bloodNodes)
      console.log(this.bloodEdges)
    },
    redraw () {
      // this.graph.destroy()
      this.nodes[0]['x'] = 10
      this.graph.refresh()
      console.log(this.graph.save())
    },
    downloadFullImage () {
      this.graph.downloadFullImage()
    },
    // 字符串截取省略号
    fittingString (str, maxWidth, fontSize) {
      const ellipsis = '...'
      const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]
    }
  }
}
</script>

<style lang="less" scoped>
#mountNode {
  margin-top: 25px;
  width: 800px;
  height: 500px;
  position: relative;
  border: 1px solid rgba(240, 240, 240, 1);
}
/deep/.g6-tooltip {
  border: 1px solid #e2e2e2;
  border-radius: 4px;
  font-size: 12px;
  color: #545454;
  background-color: rgba(255, 255, 255, 0.9);
  padding: 10px 8px;
  box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
/deep/.minimap {
  box-shadow: 0 0 0 2px rgba(0, 0, 0, .02);
  position: absolute;
  right: 0;
  bottom: 0;
  background-color: #fff;
}
.controlBar {
  margin-top: 25px;
}
</style>
